/****************************************************************
* example2
* shows the user how to setup a windowed directx application
* which clears the window to a blue color
****************************************************************/
#include <windows.h>

// include directx9
#include <d3d9.h>
#include <d3dx9tex.h>

// global variables
HINSTANCE hInst;							// application instance
HWND wndHandle;								// application window handle

LPDIRECT3D9             pD3D;				// the Direct3D Object
LPDIRECT3DDEVICE9       pd3dDevice;			// the Direct3D Device


//----------------------------------< Added for lab >----------------------------------//
static int const SCREEN_WIDTH = 800, SCREEN_HEIGHT = 600;

static int angleSun = 0, angleEarth = 0, angleEarthOrbit = 0, angleMoon = 0;

//LPD3DXMESH pMesh;     // holds the newly created mesh
LPD3DXMESH pMeshSun, pMeshEarth, pMeshMoon;		// holds mesh data for 3 for sun, Earth, and moon
D3DXMATERIAL materialSun, materialEarth, materialMoon;   // holds the material for the meshes


// camera variables
D3DXMATRIX matView;     // the view matrix
D3DXMATRIX matProj;     // the projection matrix
D3DXVECTOR3 cameraPosition = D3DXVECTOR3(0.0f, 300.0f, -250.0f);   // the position of the camera
D3DXVECTOR3 cameraLook;    // where the camera is pointing

/*************************************************************************
* createCamera
* creates a virtual camera
*************************************************************************/
void createCamera(float nearClip, float farClip)
{
	//Here we specify the field of view, aspect ration and near and far clipping planes.
	D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI/4, float(SCREEN_WIDTH)/SCREEN_HEIGHT, nearClip, farClip);
	pd3dDevice->SetTransform(D3DTS_PROJECTION, &matProj);

}

/*************************************************************************
* moveCamera
* moves the camera to a position specified by the vector passed as a 
* parameter
*************************************************************************/
void moveCamera(D3DXVECTOR3 vec)
{
	cameraPosition = vec;
}

/*************************************************************************
* pointCamera
* points the camera a location specified by the passed vector
*************************************************************************/
void pointCamera(D3DXVECTOR3 vec)
{
	cameraLook = vec;

	D3DXMatrixLookAtLH(&matView, &cameraPosition,  //Camera Position
								&cameraLook,  //Look At Position
								&D3DXVECTOR3(0.0f, 1.0f, 0.0f));  //Up Direction

	pd3dDevice->SetTransform(D3DTS_VIEW, &matView);
}
//-------------------------------------------------------------------------------------//


////////////////////////////////////////////// forward declarations
bool initWindow(HINSTANCE hInstance);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

// DirectX functions
bool initDirect3D();	
void render(void);

/*********************************************************************
* WinMain
*********************************************************************/
int WINAPI WinMain(HINSTANCE hInstance, 
				   HINSTANCE hPrevInstance, 
				   LPTSTR lpCmdLine, int nCmdShow)
{
	if (!initWindow(hInstance))
	{
		MessageBox(NULL, "Unable to create window", "ERROR", MB_OK);
		return false;
	}

	if (!initDirect3D())
	{
		MessageBox(NULL, "Unable to init Direct3D", "ERROR", MB_OK);
		return false;
	}

	// Main message loop:
	MSG msg; 
	ZeroMemory( &msg, sizeof(msg) );
	while( msg.message!=WM_QUIT )
	{
		if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
		{
			TranslateMessage( &msg );
			DispatchMessage( &msg );
		}		
		else
		{
			render();
		} 
	}

	// release the device and the direct3D object
	if( pd3dDevice != NULL) 
		pd3dDevice->Release();

	if( pD3D != NULL)
		pD3D->Release();
		
	return (int) msg.wParam;
}

/*********************************************************************
* initWindow
*********************************************************************/
bool initWindow(HINSTANCE hInstance)
{
	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX);
	wcex.style			= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= (WNDPROC)WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon			= 0;
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= NULL;
	wcex.lpszClassName	= "DirectX Lab 3 - Wk 3 Ronald Ram";
	wcex.hIconSm		= 0;
	RegisterClassEx(&wcex);

	// create the window
	wndHandle = CreateWindow("DirectX Lab 3 - Wk 3 Ronald Ram", 
							 "DirectX Lab 3 Transformations and Camera - Wk 3 Ronald Ram", 
							 WS_OVERLAPPEDWINDOW,
							 CW_USEDEFAULT, 
							 CW_USEDEFAULT, 
							 SCREEN_WIDTH, 
							 SCREEN_HEIGHT, 
							 NULL, 
							 NULL, 
							 hInstance, 
							 NULL);
   if (!wndHandle)
	  return false;
   
   ShowWindow(wndHandle, SW_SHOW);
   UpdateWindow(wndHandle);

   return true;
}

/*********************************************************************
* WndProc
*********************************************************************/
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message) 
	{
		case WM_DESTROY:
			PostQuitMessage(0);
			break;
			// handle keyboard events
		case WM_KEYDOWN:
			// switch on which key was pressed
			switch( wParam )
			{
				// handle arrow keys
				case VK_UP:
					cameraPosition.y += 1.0f;
					cameraLook.y += 1.0f;
				break;
				case VK_DOWN:
					cameraPosition.y -= 1.0f;
					cameraLook.y -= 1.0f;
				break;
				case VK_LEFT:
					cameraPosition.x -= 1.0f;
					cameraLook.x -= 1.0f;
				break;
				case VK_RIGHT:
					cameraPosition.x += 1.0f;
					cameraLook.x += 1.0f;
				break;
			}
			break;
	}
	return DefWindowProc(hWnd, message, wParam, lParam);
}

/*********************************************************************
* initDirect3D
* initializes direct3D
*********************************************************************/
bool initDirect3D()
{
	pD3D = NULL;
	pd3dDevice = NULL;

	// create the directX object
	if( NULL == ( pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
	{
		return false;
	}

	// fill the presentation parameters structure
	D3DPRESENT_PARAMETERS d3dpp; 
	ZeroMemory( &d3dpp, sizeof(d3dpp) );
	d3dpp.Windowed = TRUE;
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
	d3dpp.BackBufferCount  = 1;
	d3dpp.BackBufferHeight = SCREEN_HEIGHT;
	d3dpp.BackBufferWidth  = SCREEN_WIDTH;
	d3dpp.hDeviceWindow    = wndHandle;


	// create a default directx device
	if( FAILED( pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_REF, wndHandle,
									  D3DCREATE_SOFTWARE_VERTEXPROCESSING,
									  &d3dpp, &pd3dDevice ) ) )
	{
		return false;
	}
	//---------------------------< added for lab >---------------------------//
	pd3dDevice->SetRenderState(D3DRS_AMBIENT, D3DCOLOR_XRGB(255, 255, 255));
	pd3dDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
	//-----------------------------------------------------------------------//


	return true;
}

/*********************************************************************
* render
*********************************************************************/

void render(void)
{
	// check to make sure we have a valid Direct3D Device
	if( NULL == pd3dDevice )
		return;

	// Clear the backbuffer to a blue color
	pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,255), 1.0f, 0 );

	//--------------------------------< added for lab >----------------------------------//	
	D3DXMATRIX meshMatSun, meshMatEarth, meshMatMoon,
		meshEarthRotate, meshEarthTranslate,	// Earth movement data
		meshMoonRotate, meshMoonTranslate,		// Moon movement data
		meshSunRotate, meshSunTranslate;		// Sun movement data

	// check to make sure we have a valid Direct3D Device
	if( NULL == pd3dDevice )
			return;
	  
	createCamera(1.0f, 1000.0f);  // near clip plane, far clip plane
	moveCamera(cameraPosition);
	pointCamera(cameraLook);
	
	// Clear the backbuffer to a black color
	pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0 );

	#pragma region Sun

	D3DXCreateSphere(pd3dDevice, 30.0f, 20, 20, &pMeshSun, NULL);

	// create the Sun's material
	materialSun.MatD3D.Ambient.a = 0.0f;
	materialSun.MatD3D.Ambient.r = 1.0f;
	materialSun.MatD3D.Ambient.g = 0.0f;
	materialSun.MatD3D.Ambient.b = 0.0f;

	// set the rotation and translation for our sun
	D3DXMatrixRotationY(&meshSunRotate, D3DXToRadian(angleSun--));
	D3DXMatrixTranslation(&meshSunTranslate, 0, 0, 0);

	// multiple the scaling and rotation matrices to create the meshMat matrix
	meshMatSun = meshSunRotate * meshSunTranslate;

	// transform the object in world space
	pd3dDevice->SetTransform(D3DTS_WORLD, &meshMatSun);

	// draw Sun
	pd3dDevice->BeginScene();

	pd3dDevice->SetMaterial(&materialSun.MatD3D);	// set the material for the Sun
	pMeshSun->DrawSubset(0);						// draw the Sun mesh
	
	pd3dDevice->EndScene();

	#pragma endregion

	#pragma region Earth

	D3DXCreateSphere(pd3dDevice, 12.8f, 16, 16, &pMeshEarth, NULL);

	// create the Earth's material
	materialEarth.MatD3D.Ambient.a = 0.0f;
	materialEarth.MatD3D.Ambient.r = 0.0f;
	materialEarth.MatD3D.Ambient.g = 0.0f;
	materialEarth.MatD3D.Ambient.b = 1.0f;

	// set the rotation and translation for our earth
	angleEarthOrbit += 4;
	D3DXMatrixRotationY(&meshEarthRotate, D3DXToRadian(angleEarthOrbit));
	D3DXMatrixTranslation(&meshEarthTranslate, 0, 0, 0);
	// rotate earth
	D3DXMATRIX earthRotMat;
	D3DXMatrixMultiply(&earthRotMat, &meshEarthRotate, &meshEarthTranslate);
	// rotate earth around sun
	D3DXMatrixRotationY(&meshEarthRotate, D3DXToRadian(angleEarth++));
	D3DXMatrixTranslation(&meshEarthTranslate, 100, 0, 0);
	D3DXMatrixMultiply(&meshMatEarth, &meshEarthTranslate, &meshEarthRotate);

	// multiply orbit and rotation matrices
	D3DXMatrixMultiply(&meshMatEarth, &earthRotMat, &meshMatEarth);

	// transform the object in world space
	pd3dDevice->SetTransform(D3DTS_WORLD, &meshMatEarth);

	// draw earth
	pd3dDevice->BeginScene();

	pd3dDevice->SetMaterial(&materialEarth.MatD3D);	// set the material for the Earth
	pMeshEarth->DrawSubset(0);						// draw the Earth mesh
	
	pd3dDevice->EndScene();
	
	#pragma endregion

	#pragma region Moon

	D3DXCreateSphere(pd3dDevice, 3.5f, 8, 8, &pMeshMoon, NULL);

	// create the Moon's material
	materialMoon.MatD3D.Ambient.a = 0.0f;
	materialMoon.MatD3D.Ambient.r = 1.0f;
	materialMoon.MatD3D.Ambient.g = 1.0f;
	materialMoon.MatD3D.Ambient.b = 1.0f;

	// set moon orbit
	D3DXMatrixTranslation(&meshMoonTranslate, 25, 0, 0);
	D3DXMatrixRotationY(&meshMoonRotate, D3DXToRadian(angleMoon -= 8));	
	D3DXMatrixMultiply(&meshMatMoon, &meshMoonTranslate, &meshMoonRotate);	
	
	// multiply moon rotation matrix with earth orbit matrix to make moon orbit earth while earth rotates around sun
	D3DXMatrixMultiply(&meshMatMoon, &meshMatMoon, &meshMatEarth);

	// transform the object in world space
	pd3dDevice->SetTransform(D3DTS_WORLD, &meshMatMoon);

	// draw Moon
	pd3dDevice->BeginScene();

	pd3dDevice->SetMaterial(&materialMoon.MatD3D);	// set the material for the Moon
	pMeshMoon->DrawSubset(0);						// draw the Moon mesh
	
	pd3dDevice->EndScene();

	#pragma endregion


	// Present the backbuffer contents to the display
	pd3dDevice->Present( NULL, NULL, NULL, NULL );

	// Free up or release the resources allocated by CreateSpheres
	pMeshSun->Release();
	pMeshEarth->Release();
	pMeshMoon->Release();
	//-------------------------------------------------------------------------------//

	// Present the backbuffer contents to the display
	pd3dDevice->Present( NULL, NULL, NULL, NULL );
}
